package com.ruoyi.aitcommon.aspectj;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

import com.ruoyi.common.annotation.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ruoyi.aitcommon.anotation.ApiLog;
import com.ruoyi.aitcommon.domain.AitLog;
import com.ruoyi.aitcommon.service.IAitLogService;
import com.ruoyi.aitcommon.utils.AITConst;
import com.ruoyi.aitcommon.utils.AspectjUtils;
import com.ruoyi.aitcommon.utils.FlowUtils;
import com.ruoyi.aitcommon.utils.JSONResult;
import com.ruoyi.aitcommon.utils.JSONUtils;
import com.ruoyi.aitcommon.utils.LogUtils;
import com.ruoyi.aitcommon.utils.MapUtils;
import com.ruoyi.aitcommon.vo.FlowContextVO;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;

import cn.hutool.core.util.StrUtil;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class ApiLogAspect {
	private static final String RETCODE="code";
	private static final String RETMSG="msg";

	private static final Logger log = LogUtils.getLogger();
	@Autowired
	private IAitLogService logService;
	
	private Date startTime,endTime;
	
	// 配置织入点
    @Pointcut("@annotation(com.ruoyi.aitcommon.anotation.ApiLog)")
    public void pointCut()
    {
    }
    
    /**
	 * 切面处理逻辑
	 * 
	 * @param point 切入点
	 * @return
	 * @throws Throwable
	 */
	@Around("pointCut()")
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
		try {
//			方法执行前
//	      	获取参数
			Object[] args = AspectjUtils.getParmValues(point);
			this.startTime=new Date();
//		    执行目标方法
			Object returnValue = point.proceed(args);
//		    方法执行后
			this.endTime=new Date();
//		    处理日志
			handleLog(point, null, returnValue);
//			返回
			return returnValue;
		} catch (Exception ex) {
//		    方法执行后
			this.endTime=new Date();
//		    处理日志
			handleLog(point, ex, null);
		} finally {

		}
		return null;
	}
	
    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
//    @AfterReturning(pointcut = "pointCut()", returning = "jsonResult")
//    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
//    {
//        handleLog(joinPoint, null, jsonResult);
//    }
    
    /**
     * 拦截异常操作
     * 
     * @param joinPoint 切点
     * @param ex 异常
     */
//    @AfterThrowing(value = "pointCut()", throwing = "ex")
//    public void doAfterThrowing(JoinPoint joinPoint, Exception ex)
//    {
//        handleLog(joinPoint, ex, null);
//    }

	/**
	 * 日志处理
	 * @param joinPoint
	 * @param ex
	 * @param jsonResult
	 */
    protected void handleLog(final JoinPoint joinPoint, final Exception ex, Object jsonResult)
    {

    	AitLog aitlog=new AitLog();
        try
        {
			//数据库日志
            // 获得注解
        	ApiLog annLog=getAnnotation(joinPoint,ApiLog.class);
            if (annLog == null)
                return;
//            错误消息
            if (ex != null)
            	jsonResult=JSONResult.error(StrUtil.sub(ex.getMessage(), 0, AITConst.STRINGSIZE-1));
            
            // *========数据库日志=========*//
            aitlog.setLogType("接口日志");
            aitlog.setStartDate(startTime);
            aitlog.setEndDate(endTime);
            long run=this.endTime.getTime()-this.startTime.getTime();
            aitlog.setRunTime(run);
            // 请求的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            aitlog.setOperIp(ip);
//            请求URL
			String fullURL = getFullURL();
//            aitlog.setOperUrl(ServletUtils.getRequest().getRequestURI());
			aitlog.setOperUrl(fullURL);
            // 设置标题
            aitlog.setApiName(annLog.value());
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            aitlog.setApiMethod(className + "." + methodName + "()");
            // 设置请求方式
            aitlog.setReqMethod(ServletUtils.getRequest().getMethod());
//        	流程名称，流程ID
            setFlowFields(aitlog);
//            设置参数
            Object[] args = joinPoint.getArgs();
            if(args.length>0&&args[0]!=null) {
            	aitlog.setInParam(args[0].toString());
//              设置关键参数字段
                setParFields(aitlog, args[0], annLog.parFields());
            }
//            设置返回值
            if(jsonResult!=null) {
            	aitlog.setRetParam(jsonResult.toString());
//            	设置关键返回字段
            	setRetFields(aitlog,jsonResult,annLog.retFields());
            }
//          返回码,返回消息  retcode,retmsg
            Map<?, ?> jmap=null;
            if(jsonResult instanceof Map) {
            	jmap=(Map<?, ?>)jsonResult;
            }else if(jsonResult instanceof String) {
            	jmap = JSONUtils.parseObject(jsonResult.toString());
            }
            if(jmap.containsKey(RETCODE)&&jmap.get(RETCODE)!=null)
        		aitlog.setRetcode(jmap.get(RETCODE).toString());
        	if(jmap.containsKey(RETMSG)&&jmap.get(RETMSG)!=null)
        		aitlog.setRetmsg(jmap.get(RETMSG).toString());
            
            // Log文件日志
			LogUtils.httpInfo("===========接口日志=============");
			LogUtils.httpInfo("接口地址：{}",fullURL);
			LogUtils.httpInfo("接口名称/方法：{}/{}",aitlog.getApiName(),aitlog.getApiMethod());
			LogUtils.httpInfo("返回码/消息：{}/{}",aitlog.getRetcode(),aitlog.getRetmsg());
			LogUtils.httpInfo("接收数据：\r\n{}",aitlog.getInParam());
			LogUtils.httpInfo("返回数据：\r\n{}",aitlog.getRetParam());
			LogUtils.httpInfo("===========接口日志=============");
            //构造flow简报
            FlowUtils.addBulletin("接口名称/方法：{}/{}",aitlog.getApiName(),aitlog.getApiMethod());
            FlowUtils.addBulletin("返回码/消息：{}/{}",aitlog.getRetcode(),aitlog.getRetmsg());
			//设置字段大小
			aitlog.setInParam(StringUtils.substring(aitlog.getInParam(), 0, AITConst.BIGSTRINGSIZE-1));
			aitlog.setRetParam(StringUtils.substring(aitlog.getRetParam(), 0, AITConst.BIGSTRINGSIZE-1));
			aitlog.setRetmsg(StringUtils.substring(aitlog.getRetmsg(), 0, AITConst.BIGSTRINGSIZE-1) );
        }catch (Exception exp){
            // 记录本地异常日志
            log.error("=====ApiLog通知异常=====");
            log.error("异常信息:{}", exp.getMessage());
            log.error("接收数据：{}",joinPoint.getArgs());
            
            FlowUtils.addBulletin("异常信息:{}", exp.getMessage());
//            在AitFlowAspect打印异常
//            exp.printStackTrace();
        }finally {
        	// 保存数据库
            logService.insertAitLog(aitlog);
        }
    }

	private static HttpServletRequest getRequest() {
		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
	}

	private static String getFullURL() {
		HttpServletRequest request = getRequest();
		String requestURL = request.getRequestURL().toString();
		String queryString = request.getQueryString();
		String fullURL = requestURL + (queryString != null ? "?" + queryString : "");
		return fullURL;
	}

	//    设置flow信息
    private void setFlowFields(AitLog aitlog) {
    	FlowContextVO flowinfo = FlowUtils.getFlowContext();
    	if(flowinfo!=null) {
    		aitlog.setFlowId(flowinfo.getFlowID());
    		aitlog.setFlowName(flowinfo.getFlowName());
    	}
	}

	//设置json中的关键参数
	private void setParFields(AitLog aitlog, Object json, String[] parFields) throws Exception {
		if(parFields.length>0) {
			for(int i=0;i<parFields.length;i++) {
				Object parObj = MapUtils.getPathObject(parFields[i], json.toString());
				if(parObj!=null) {
					String substring = StringUtils.substring(parObj.toString(), 0, AITConst.STRINGSIZE);
					if(i==0)
						aitlog.setParField0(substring);
					else if(i==1)
						aitlog.setParField1(substring);
					else if(i==2)
						aitlog.setParField2(substring);
					else if(i==3)
						aitlog.setParField3(substring);
					else if(i==4)
						aitlog.setParField4(substring);
					else if(i==5)
						aitlog.setParField5(substring);
					else if(i==6)
						aitlog.setParField6(substring);
					else if(i==7)
						aitlog.setParField7(substring);
					else if(i==8)
						aitlog.setParField8(substring);
					else if(i==9)
						aitlog.setParField9(substring);
				}
			}
		}
	}
	
//	设置返回json中的关键参数
	private void setRetFields(AitLog aitlog, Object json, String[] retFields) throws Exception {
		if(retFields.length>0) {
			for(int i=0;i<retFields.length;i++) {
				Object retObj = MapUtils.getPathObject(retFields[i], json.toString());
				if(retObj!=null) {
					String substring = StringUtils.substring(retObj.toString(), 0, AITConst.STRINGSIZE-1);
					if(i==0)
						aitlog.setRetField0(substring);
					else if(i==1)
						aitlog.setRetField1(substring);
					else if(i==2)
						aitlog.setRetField2(substring);
					else if(i==3)
						aitlog.setRetField3(substring);
					else if(i==4)
						aitlog.setRetField4(substring);
					else if(i==5)
						aitlog.setRetField5(substring);
					else if(i==6)
						aitlog.setRetField6(substring);
					else if(i==7)
						aitlog.setRetField7(substring);
					else if(i==8)
						aitlog.setRetField8(substring);
					else if(i==9)
						aitlog.setRetField9(substring);
				}
			}
		}
	}
    
    /**
     * 是否存在注解，如果存在就获取
     */
    private <T extends Annotation> T getAnnotation(JoinPoint joinPoint,Class<T> clazz) throws Exception
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return method.getAnnotation(clazz);
        }
        return null;
    }
    
}
